package io.hellsinger.vortex.ui.screen.storage.editor

import io.hellsinger.filesystem.ByteArrayBuilder
import io.hellsinger.filesystem.linux.LinuxOperationOptions.Access
import io.hellsinger.filesystem.linux.LinuxOperationOptions.Open
import io.hellsinger.filesystem.size.SizeUnit
import io.hellsinger.viewmodel.BitViewModel
import io.hellsinger.viewmodel.intent
import io.hellsinger.vortex.Dispatchers
import io.hellsinger.vortex.bit.Bits
import io.hellsinger.vortex.data.Characters
import io.hellsinger.vortex.data.Line
import io.hellsinger.vortex.data.model.PathItem
import io.hellsinger.vortex.domain.repository.StorageRepository
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import java.nio.charset.Charset

class StorageFileEditorViewModel(
    private val dispatchers: Dispatchers,
    private val repository: StorageRepository,
) : BitViewModel<UiState>(scopeName = "File Editor scope"),
    Bits {
    private val state = MutableUiState()

    fun load(item: PathItem) =
        intent {
            state.item = item
            val sourceSize = item.attrs.size

            // TODO: Add map editing
            if (sourceSize > SizeUnit.create(4, SizeUnit.MiB)) {
                update(UPDATE_MESSAGE addFlag REASON_ITEM_TOO_LARGE)
                return@intent
            }

            val checkResult =
                repository.check(path = item.path, flags = Access.READABLE or Access.EXISTENCE)

            if (checkResult != 0) {
                when {
                    checkResult hasFlag Access.READABLE -> {
                        update(UPDATE_MESSAGE addFlag REASON_ITEM_IS_NOT_READABLE)
                        return@intent
                    }

                    checkResult hasFlag Access.EXISTENCE -> {
                        update(UPDATE_MESSAGE addFlag REASON_ITEM_DOES_NOT_EXIST)
                        return@intent
                    }
                }
            }

            val loading =
                launch {
                    delay(1000L)
                    update(UPDATE_LOADING)
                    return@launch
                }

            val builder = ByteArrayBuilder(100)

            repository
                .getBytes(
                    path = item.path,
                    flags = Open.READ_ONLY or Open.NON_BLOCKING,
                ).onEach { byte ->
                    when (byte) {
                        Characters.Special.NEW_LINE_BYTE -> {
                            state.lines += Line(builder.toString(Charset.forName(state.encoding.original)))
                            builder.reset()
                        }

                        Characters.Special.RETURN_BYTE -> {
                        }

                        else -> builder.append(byte)
                    }
                }.onCompletion {
                    builder.toString(Charset.forName(state.encoding.original))
                }.flowOn(dispatchers.io)
                .collect()
            loading.cancel()
            update(UPDATE_EDITOR)
        }

    override fun getUiState(): UiState = state

    companion object {
        const val UPDATE_EDITOR = 1
        const val UPDATE_LOADING = 1 shl 1
        const val UPDATE_MESSAGE = 1 shl 2

        const val REASON_ITEM_TOO_LARGE = 3 shl 24
        const val REASON_ITEM_DOES_NOT_EXIST = 3 shl 25
        const val REASON_ITEM_IS_NOT_READABLE = 3 shl 26
    }
}
